/**
 * FileName: OperationAdd
 *
 * @author: 阿张
 * Date:     2021/3/3 15:09
 */
package com.qx.service;

import com.qx.pojo.Num;
import com.qx.pojo.Salo;
import com.qx.pojo.ScUser;
import com.qx.service.Distribution;
import com.qx.util.GsonUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Service
public class OperationAdd implements Distribution {



    /**
     * 1.总金额不超过总共可砍的价格*100  单位是分
     * 2.每次砍价都能砍到金额，最低不能低于1分，最大金额不能超过（总共可砍的价）*100
     */
    private static final int MINMONEY = 1;
    private static final int MAXMONEY = 10 * 100;



    /**
     * 这里为了避免某一次砍价占用大量资金，设定非最后一次砍价的最大金额，
     * 把他设置为砍价金额平均值的N倍
     */
    private static final double TIMES = 3.1;

    /**
     * 砍价合法性校验
     * 控制金额过小或者过大
     */
    private boolean isRight(int money, int surplusKnife) {
        double avg = money / surplusKnife;
        //小于最小金额
        if (avg < MINMONEY) {
            return false;
        } else if (avg > MAXMONEY) {
            return false;
        }
        return true;
    }

    /**
     * 随机分配一个金额
     *
     * @param money：砍价金额
     * @param minS：最小金额
     * @param maxS：最大金额
     * @param surplusKnife
     * @return
     */
    private int randomReducePrice(int money, int minS, int maxS, int surplusKnife) {
        //若只剩一次，则直接返回
        if (surplusKnife == 1) {
            return money;
        }
        //如果最大金额和最小金额相等，直接返回金额
        if (minS == maxS) {
            return minS;
        }
        int max = Math.min(maxS, money);
        //分配砍价正确情况，允许砍价的最大值
        int maxY = money - (surplusKnife - 1) * minS;
        //分配砍价正确情况，允许砍价最小值
        int minY = money - (surplusKnife - 1) * maxS;
        //随机产生砍价的最小值
        int min = Math.max(minS, minY);
        //随机产生砍价的最大值
        max = Math.min(max, maxY);
        //随机产生一个砍价
        return (int) Math.rint(Math.random() * (max - min) + min);
    }

    /**
     * 砍价----返回一个随机金额
     *
     * @param money 待砍总价
     * @param surplusKnife 待砍刀数
     * @return
     */
    private int splitReducePrice(int money, int surplusKnife) {
        //金额合法性分析
        if (!isRight(money, surplusKnife)) {
            return 0;
        }
        //每次砍价的最大的金额为平均金额的TIMES倍
        int max = (int) (money * TIMES / surplusKnife);
        max = Math.min(max, MAXMONEY);
        int one = randomReducePrice(money, MINMONEY, max, surplusKnife);
        return one;
    }

    /**
     获取区间内的一个随机整数
     原理:要得到的随机数的范围是[110,130]，返回的伪随机数的范围是[0,130)，
     也即[0,130-1];对得到的这个数模长度是129，于是得到的数的范围是[0,129]；
     将得到的随机数取余，控制最大值为区间的差数[max-min]，这样就可以得到一个区间差数内的一个随机数[min-min，max-min]，
     再把这个随机数加上最小值，就可以得出[min，max]区间内的一个随机数
     */
    private int getRandom(int min, int max){
        Random random = new Random();
        int r = random.nextInt(max) % (max - min + 1) + min;
        return r;
    }

    /**
     * 该方法用于用户砍价开始时初始化砍价配置
     * @param needMoney 待砍总价
     * @param minKnife 最少需要砍价刀数
     * @param maxKnife 最多需要砍价刀数
     * */
    private String getBargainConfig(int needMoney, int minKnife, int maxKnife,ScUser hdUser, Salo salo) {
        try {
            //获取随机总刀数
            int knifeNum;
            if (salo.getSaloNum() == 0){
                knifeNum = getRandom(minKnife,maxKnife);
                salo.setSaloNum(knifeNum);
            }
            knifeNum = salo.getSaloNum();
            System.out.println("随机刀数:"+knifeNum);
            Map<String, String> bargainRecordMap = new HashMap<String, String>();
            bargainRecordMap.put("needMoney",needMoney + "");
            bargainRecordMap.put("knifeNum",knifeNum + "");
            bargainRecordMap.put("surplusMoney",needMoney + "");
            bargainRecordMap.put("surplusKnife",knifeNum + "");

            hdUser.setRemake(GsonUtil.toJson(bargainRecordMap));
        }catch (Exception e){
            return "102";
        }
        return "00";
    }

    /**
     * 预砍价----获取一个随机金额
     * @param surplusMoney 当前待砍金额
     * @param surplusKnife 当前待砍刀数
     * */
    private int bargain(int surplusMoney, int surplusKnife) {
        //预算这次砍掉的钱
        int bargainMoney = 0;
        try {
            bargainMoney = splitReducePrice(surplusMoney, surplusKnife);
            if (surplusMoney < bargainMoney){
                bargainMoney = surplusMoney;
            }
            return bargainMoney;
        }catch (Exception e){
            e.printStackTrace();
            return -1;
        }
    }

    @Transactional(propagation= Propagation.REQUIRED)
    @Override
    public Num doOperation(int needMoney, int minKnife, int maxKnife, ScUser hdUser , Salo salo) {
        /*int needMoney = 10000;
        int minKnife = 50;
        int maxKnife = 60;hdUser.getId()*/
        String sul = "";

        sul = getBargainConfig(needMoney, minKnife, maxKnife, hdUser,salo);

        if (!"00".equals(sul)){
            System.out.println("系统异常");
        }
        String remark = hdUser.getRemake();
        System.out.println(remark);
        Map<String, String> remarkMap = GsonUtil.fromJson(remark, Map.class);
        int knifeNum = Integer.parseInt(remarkMap.get("knifeNum"));
        int surplusMoney = Integer.parseInt(remarkMap.get("surplusMoney"));
        int surplusKnife = Integer.parseInt(remarkMap.get("surplusKnife"));
        int count1 = 0;
        int count2 = 0;
        //for (int i = 0;i < knifeNum;i++){
            int res =  bargain(surplusMoney,surplusKnife);
            if(0 > res){
                System.out.println("系统异常");
            }
            surplusMoney -= res;
            surplusKnife --;

            count1 += res;
            count2 ++;

            System.out.println("砍掉"+(double)res/100+"元"+"剩余"+(double)surplusMoney/100+"元"+"剩余"+surplusKnife+"刀");

            remarkMap.put("surplusMoney",surplusMoney + "");
            remarkMap.put("surplusKnife",surplusKnife + "");
            hdUser.setRemake(GsonUtil.toJson(remarkMap));
        //}
        System.out.println("总共砍了"+(double)count1/100+"元-----"+"总共砍了"+count2+"刀");
        Num num = new Num();
        num.setKnife((double)count1/100);
        num.setNum(surplusKnife);
        return num;
    }
}
